#include <stdexcept>
#include <memory>
#include <vector>
#include <iterator> 	// for distance
#include <cctype> 	// for toupper, isxdigit
#include <iostream>
#include <string>
using namespace std;

//源码来自cgicc-3.2.13.tar https://ftp.gnu.org/gnu/cgicc/

// case-insensitive string comparison
// This code based on code from 
// "The C++ Programming Language, Third Edition" by Bjarne Stroustrup
bool 
cgicc_stringsAreEqual(const std::string& s1, 
		       const std::string& s2)
{
  std::string::const_iterator p1 = s1.begin();
  std::string::const_iterator p2 = s2.begin();
  std::string::const_iterator l1 = s1.end();
  std::string::const_iterator l2 = s2.end();

  while(p1 != l1 && p2 != l2) {
    if(std::toupper(*(p1++)) != std::toupper(*(p2++)))
      return false;
  }

  return (s2.size() == s1.size()) ? true : false;
}

// case-insensitive string comparison
bool 
cgicc_stringsAreEqual(const std::string& s1, 
		       const std::string& s2,
		       size_t n)
{
  std::string::const_iterator p1 = s1.begin();
  std::string::const_iterator p2 = s2.begin();
  bool good = (n <= s1.length() && n <= s2.length());
  std::string::const_iterator l1 = good ? (s1.begin() + n) : s1.end();
  std::string::const_iterator l2 = good ? (s2.begin() + n) : s2.end();

  while(p1 != l1 && p2 != l2) {
    if(std::toupper(*(p1++)) != std::toupper(*(p2++)))
      return false;
  }
  
  return good;
}

std::string
cgicc_charToHex(char c)
{
  std::string result;
  char first, second;

  first = (c & 0xF0) / 16;
  first += first > 9 ? 'A' - 10 : '0';
  second = c & 0x0F;
  second += second > 9 ? 'A' - 10 : '0';

  result.append(1, first);
  result.append(1, second);
  
  return result;
}

char
cgicc_hexToChar(char first,
		 char second)
{
  int digit;
  
  digit = (first >= 'A' ? ((first & 0xDF) - 'A') + 10 : (first - '0'));
  digit *= 16;
  digit += (second >= 'A' ? ((second & 0xDF) - 'A') + 10 : (second - '0'));
  return static_cast<char>(digit);
}

/* 
   From the HTML standard: 
   <http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1>

   application/x-www-form-urlencoded  

   This is the default content type. Forms submitted with this content
   type must be encoded as follows:

   1. Control names and values are escaped. Space characters are
   replaced by `+', and then reserved characters are escaped as
   described in [RFC1738], section 2.2: Non-alphanumeric characters
   are replaced by `%HH', a percent sign and two hexadecimal digits
   representing the ASCII code of the character. Line breaks are
   represented as "CR LF" pairs (i.e., `%0D%0A').  
   2. The control names/values are listed in the order they appear in
   the document. The name is separated from the value by `=' and
   name/value pairs are separated from each other by `&'.


   Note RFC 1738 is obsoleted by RFC 2396.  Basically it says to
   escape out the reserved characters in the standard %xx format.  It
   also says this about the query string:
   
   query         = *uric
   uric          = reserved | unreserved | escaped
   reserved      = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
   "$" | ","
   unreserved    = alphanum | mark
   mark          = "-" | "_" | "." | "!" | "~" | "*" | "'" |
   "(" | ")"
   escaped = "%" hex hex */
 
std::string
cgicc_form_urlencode(const std::string& src)
{
  std::string result;
  std::string::const_iterator iter;
  
  for(iter = src.begin(); iter != src.end(); ++iter) {
    switch(*iter) {
    case ' ':
      result.append(1, '+');
      break;
      // alnum
    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
    case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
    case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
    case 'V': case 'W': case 'X': case 'Y': case 'Z':
    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
    case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
    case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
    case 'v': case 'w': case 'x': case 'y': case 'z':
    case '0': case '1': case '2': case '3': case '4': case '5': case '6':
    case '7': case '8': case '9':
      // mark
    case '-': case '_': case '.': case '!': case '~': case '*': case '\'': 
    case '(': case ')':
      result.append(1, *iter);
      break;
      // escape
    default:
      result.append(1, '%');
      result.append(cgicc_charToHex(*iter));
      break;
    }
  }
  
  return result;
}

std::string
cgicc_form_urldecode(const std::string& src)
{
  std::string result;
  std::string::const_iterator iter;
  char c;

  for(iter = src.begin(); iter != src.end(); ++iter) {
    switch(*iter) {
    case '+':
      result.append(1, ' ');
      break;
    case '%':
      // Don't assume well-formed input
      if(std::distance(iter, src.end()) >= 2
	 && std::isxdigit(*(iter + 1)) && std::isxdigit(*(iter + 2))) {
	c = *++iter;
	result.append(1, cgicc_hexToChar(c, *++iter));
      }
      // Just pass the % through untouched
      else {
	result.append(1, '%');
      }
      break;
    
    default:
      result.append(1, *iter);
      break;
    }
  }
  
  return result;
}

//c++实现urlencode和urldecode
int main()
{
	//std::string url="https://www.baidu.com/s?wd=%E6%98%93%E8%AF%AD%E8%A8%80&rsv_spt=1&rsv_iqid=0xfda263f30003d977&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&rqlang=cn&tn=baiduhome_pg&rsv_enter=1&oq=cgicc&inputT=2976&rsv_t=fdcfmHLzKPCrEmQpelBBANsdBeI31NK2JXlFyWRL%2FwRksRGm8%2BJoGlrjxMyZA1dsa4eV&rsv_pq=c26a86f10003aa86&rsv_sug3=15&rsv_sug1=15&rsv_sug7=100&rsv_sug2=0&rsv_sug4=2976";
	string url="https://so.csdn.net/so/search/s.do?q=java&t=blog&u=earbao";
	std::cout<<"================="<<std::endl;
	std::cout<<url<<std::endl;
	
	string line1=cgicc_form_urldecode(url);
	std::cout<<"================="<<std::endl;
	std::cout<<line1<<std::endl;
	
	
	string line2=cgicc_form_urlencode(url);
	std::cout<<"================="<<std::endl;
	std::cout<<line2<<std::endl;
	
	string line3=cgicc_form_urldecode(line2);
	std::cout<<"================="<<std::endl;
	std::cout<<line3<<std::endl;
	return 0;
	
}